package org.aeon.gamechatnest.socket;

import jakarta.annotation.Resource;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.aeon.gamechatnest.common.Constant;
import org.aeon.gamechatnest.common.response.ResultBuild;
import org.aeon.gamechatnest.configuration.CustomSpringConfigurator;
import org.aeon.gamechatnest.configuration.thread.MessageSendHandler;
import org.aeon.gamechatnest.dto.ChatMessageAddRequest;
import org.aeon.gamechatnest.dto.SecurityUserDto;
import org.aeon.gamechatnest.dto.UserCacheDto;
import org.aeon.gamechatnest.pojo.ChatMessage;
import org.aeon.gamechatnest.service.ChannelChatMessageService;
import org.aeon.gamechatnest.service.PrivateChatMessageService;
import org.aeon.gamechatnest.util.JsonUtil;
import org.aeon.gamechatnest.util.JwtUtil;
import org.aeon.gamechatnest.util.RedisCache;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Date;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author MissingAeon
 * @date 2024/5/20 3:57
 * @description WebSocket服务端 用于接收客户端发送的消息
 */
@ServerEndpoint(value = "/chat/message", configurator = CustomSpringConfigurator.class)
@Slf4j
@Component
@Scope("prototype")
public class ChatMessageServer {
    /**
     * 存储所有在线客户端的websocket会话，key是userId，value是ChatMessageClient
     */
    private static final ConcurrentHashMap<Long, ChatMessageClient> webSocketMap = new ConcurrentHashMap<>();
    /**
     * 当前在线人数
     */
    private static final AtomicLong sessionCount = new AtomicLong(0);

    private Long userId;

    private ChatMessageClient client;

    @Resource
    private MessageSendHandler messageSendThread;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private PrivateChatMessageService privateChatMessageService;

    @Autowired
    private ChannelChatMessageService channelChatMessageService;

    // 打开的时候需要执行的操作，1：把用户session放入session集合中
    @OnOpen
    public void onOpen(Session session) {
        client = new ChatMessageClient();
        client.setSession(session);

//        this.responseQueue = new ConcurrentLinkedDeque<>();

        long count = sessionCount.incrementAndGet();
        log.info("连接成功！当前连接ip：{}，当前共连接：{}", session.getRequestURI().toString(), count);
    }


    // 接收到消息需要执行的操作，解析出来json数据，然后转发给另一个好友
    @OnMessage
    public void onMessage(String message) throws IOException {
        // 检查身份为新增的，如果是，则验证身份
        if (Objects.isNull(userId) || !webSocketMap.containsKey(userId)) {
            verifyIdentity(message);
            return;
        }
        receiveMessages(message);
    }

    /**
     * 验证身份
     *
     * @param message
     */
    private void verifyIdentity(String message) throws IOException {

        String username = jwtUtil.getUserFromToken(message);
        UserCacheDto userCache = redisCache.getCacheObject2(Constant.USER_TOKEN_CACHE_PREFIX + username);

        if (Objects.isNull(userCache)) {
            String res = JsonUtil.obj2String(ResultBuild.result().throwAnError().message("身份验证失败").build());
            client.getSession().getAsyncRemote().sendText(res);
            client.getSession().close();
            return;
        }
        SecurityUserDto securityUser = new SecurityUserDto();
        securityUser.setUserCache(userCache);

        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        // 获取当前登录用户id
        this.userId = userCache.getUser().getUserId();

        // 设置client参数
        client.setResponseQueue(new ConcurrentLinkedDeque<>());
        webSocketMap.put(userId, client);

        // 绑定消息队列
        messageSendThread.bindQueue(client);

        // 添加响应消息
        // 获取对话id列表PrivateChatMessageService
        client.getResponseQueue().addFirst(privateChatMessageService.syncMessage(userId));
        client.getResponseQueue().addFirst(channelChatMessageService.syncMessage(userId));
        client.getResponseQueue().addFirst(ResultBuild.result().successful().message("身份验证成功").build());
    }

    /**
     * 接收消息
     *
     * @param message
     */
    private void receiveMessages(String message) {
        // 获取用户消息
        ChatMessageAddRequest chatMessageAddRequest = JsonUtil.string2Obj(message, ChatMessageAddRequest.class);
        if (Objects.isNull(chatMessageAddRequest)) {
            client.getResponseQueue().addFirst(ResultBuild.result().throwAnError().message("消息格式错误").build());
            return;
        }

        // 转为聊天消息
        ChatMessage chatMessage = new ModelMapper().map(chatMessageAddRequest, ChatMessage.class);

        // 设置消息属性
        chatMessage.setId(UUID.randomUUID().toString());
        chatMessage.setFromUser(userId);
        chatMessage.setCreateTime(new Date().getTime());
        chatMessage.setIsDelete(0);

        // 匹配私信编号
        switch (chatMessageAddRequest.getTargetIdentifier()) {
            // 私信处理消息
            case 0 -> privateChatMessageService.handleMessage(webSocketMap, chatMessage);
            // 频道处理消息
            case 1 -> channelChatMessageService.handleMessage(webSocketMap, chatMessage);
            // 默认处理消息
            default ->
                    client.getResponseQueue().addFirst(ResultBuild.result().throwAnError().message("非法消息").build());
        }
    }

    // 连接关闭时做的操作，1：从session集合中移除session。2：更新用户状态为不在线
    @OnClose
    public void onClose() {
        handleDisconnection(userId);
    }

    // 连接发生错误时执行的操作，打印日志
    @OnError
    public void onError(Throwable throwable) {
        log.error("用户{}的连接发生错误{}", userId, throwable.getMessage());
        handleDisconnection(userId);
    }

    private void handleDisconnection(Long userId) {
        long count = sessionCount.decrementAndGet();
        log.info("用户{}的连接已关闭，还剩{}个连接", userId, count);
        try {
            if (Objects.isNull(userId))
                return;
            ChatMessageClient remove = webSocketMap.remove(userId);
            // 移除消息队列
            messageSendThread.unbindQueue(remove);
            if (Objects.nonNull(remove)) {
                remove.getSession().close();
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }
}
